﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Linq;

namespace Common.JWT
{
    public static class JwtHelper
    {
        /// <summary>
        /// MD5
        /// </summary>
        /// <param name="str"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string MD5(string str, int length = 16)
        {
            byte[] bytes = Encoding.Default.GetBytes(str);
            byte[] buffer2 = new MD5CryptoServiceProvider().ComputeHash(bytes);
            if (length == 16)
            {
                return BitConverter.ToString(buffer2).Replace("-", "").ToLower().Substring(8, 16);
            }
            return BitConverter.ToString(buffer2).Replace("-", "").ToLower();
        }

        /// <summary>
        /// base64encode for safe-url
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Base64UrlEncode(byte[] input)
        {
            var output = Convert.ToBase64String(input);
            //去掉=
            output = output.Split('=')[0];
            //替换+和/
            output = output.Replace('+', '-');
            output = output.Replace('/', '_');
            return output;
        }

        /// <summary>
        /// base64decode for safe-url
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static byte[] Base64UrlDecode(string input)
        {
            var output = input;
            //替换-和_
            output = output.Replace('-', '+');
            output = output.Replace('_', '/');
            switch (output.Length % 4)
            {
                case 0: break;
                case 2: output += "=="; break;
                case 3: output += "="; break;
                default: throw new FormatException("Illegal base64url string!");
            }
            var converted = Convert.FromBase64String(output);
            return converted;
        }

        /// <summary>
        /// 获取加密类型
        /// </summary>
        /// <param name="algorithm">加密类型</param>
        /// <returns></returns>
        public static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
        {
            switch (algorithm)
            {
                case "HS160": return JwtHashAlgorithm.HS160;
                case "HS256": return JwtHashAlgorithm.HS256;
                case "HS384": return JwtHashAlgorithm.HS384;
                case "HS512": return JwtHashAlgorithm.HS512;
                default: throw new Exception("Algorithm not supported.");
            }
        }
        /// <summary>
        /// 计算JwtHash
        /// </summary>
        /// <returns></returns>
        public static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> GetHashAlgorithms()
        {
            return new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>>
            {
                { JwtHashAlgorithm.HS160, (key, value) => { using (var sha = new HMACSHA1(key)) { return sha.ComputeHash(value); } } },
                { JwtHashAlgorithm.HS256, (key, value) => { using (var sha = new HMACSHA256(key)) { return sha.ComputeHash(value); } } },
                { JwtHashAlgorithm.HS384, (key, value) => { using (var sha = new HMACSHA384(key)) { return sha.ComputeHash(value); } } },
                { JwtHashAlgorithm.HS512, (key, value) => { using (var sha = new HMACSHA512(key)) { return sha.ComputeHash(value); } } }
            };
        }
        /// <summary>
        /// 获取指定Hash实例
        /// </summary>
        /// <param name="algorithm"></param>
        /// <returns></returns>
        public static Func<string, byte[], HashAlgorithm> GetVerityDataHash()
        {
            return new Func<string, byte[], HashAlgorithm>((algorithm, key) =>
            {
                switch (algorithm)
                {
                    case "HS160": return new HMACSHA1(key);
                    case "HS256": return new HMACSHA256(key);
                    case "HS384": return new HMACSHA384(key);
                    case "HS512": return new HMACSHA512(key);
                    default: throw new ArgumentNullException("Algorithm not supported.");
                }
            });
        }
        /// <summary>
        /// 获取指定Hash实例
        /// </summary>
        /// <param name="algorithm"></param>
        /// <returns></returns>
        public static Func<string, HashAlgorithm> GetVerityDataHash(JwtHashAlgorithm hashType)
        {
            return new Func<string, HashAlgorithm>((key) =>
            {
                switch (hashType)
                {
                    case JwtHashAlgorithm.HS160: return new HMACSHA1(Encoding.UTF8.GetBytes(key));
                    case JwtHashAlgorithm.HS256: return new HMACSHA256(Encoding.UTF8.GetBytes(key));
                    case JwtHashAlgorithm.HS384: return new HMACSHA384(Encoding.UTF8.GetBytes(key));
                    case JwtHashAlgorithm.HS512: return new HMACSHA512(Encoding.UTF8.GetBytes(key));
                    default: throw new ArgumentNullException("Algorithm not supported.");
                }
            });
        }
        /// <summary>
        /// 签名数据Hash
        /// </summary>
        /// <param name="hashType">hash类型</param>
        /// <param name="key">签名Key</param>
        /// <returns></returns>
        public static Func<string, string> SetDataHash(JwtHashAlgorithm hashType, string key)
        {
            return new Func<string, string>((value) =>
            {
                var result = string.Empty;
                if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
                {
                    return result;
                }
                var byteKey = Encoding.UTF8.GetBytes(key);
                var byteValue = Encoding.UTF8.GetBytes(value);
                switch (hashType)
                {
                    case JwtHashAlgorithm.HS160:
                        using (var sha = new HMACSHA1(byteKey))
                        {
                            var resultArr = sha.ComputeHash(byteValue);
                            foreach (var s in resultArr)
                            {
                                result += s.ToString("x2");
                            }
                        }
                        break;
                    case JwtHashAlgorithm.HS256:
                        using (var sha = new HMACSHA256(byteKey))
                        {
                            var resultArr = sha.ComputeHash(byteValue);
                            foreach (var s in resultArr)
                            {
                                result += s.ToString("x2");
                            }
                        }
                        break;
                    case JwtHashAlgorithm.HS384:
                        using (var sha = new HMACSHA384(byteKey))
                        {
                            var resultArr = sha.ComputeHash(byteValue);
                            foreach (var s in resultArr)
                            {
                                result += s.ToString("x2");
                            }
                        }
                        break;
                    case JwtHashAlgorithm.HS512:
                        using (var sha = new HMACSHA512(byteKey))
                        {
                            var resultArr = sha.ComputeHash(byteValue);
                            foreach (var s in resultArr)
                            {
                                result += s.ToString("x2");
                            }
                        }
                        break;
                }
                return result;
            });
        }
    }
}
